package org.lboot.starter.handler;

import cn.hutool.core.lang.Validator;
import lombok.extern.slf4j.Slf4j;
import org.lboot.core.domain.ErrMsg;
import org.lboot.core.domain.ResponseDTO;
import org.lboot.core.exception.AsyncBusinessException;
import org.lboot.core.exception.BusinessException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.servlet.NoHandlerFoundException;

import java.io.FileNotFoundException;
import java.nio.file.AccessDeniedException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author kindear
 * 定义异常处理规则
 */
@Slf4j
@RestControllerAdvice
// 默认不加载，配置 true 加载
@ConditionalOnProperty(prefix = "lucy.exception",name = "handle",havingValue = "true",matchIfMissing=true)
public class LucyExceptionHandler {

    public String exceptionLocation(Exception ex){
        log.error(ex.getMessage(),ex);
        // 获取异常堆栈跟踪
        StackTraceElement[] stackTrace = ex.getStackTrace();

        if (stackTrace != null && stackTrace.length > 0) {
            // 获取发生异常的类名
            String className = stackTrace[0].getClassName();

            // 获取发生异常的方法名
            String methodName = stackTrace[0].getMethodName();

            // 获取异常发生的行号
            int lineNumber = stackTrace[0].getLineNumber();

            return className+"[" + methodName + "]:" + lineNumber;
        }
        return null;
    }


    @ExceptionHandler(BusinessException.class)
    public ResponseDTO<Object> handleBusinessException(BusinessException ex){
        HttpStatus status = HttpStatus.resolve(ex.getCode());
        ErrMsg errMsg = ex.getErrMsg();
        errMsg.setStatusCode(ex.getCode());
        // 如果无业务错误类定义，直接返回错误信息
        if (Validator.isEmpty(errMsg)){
            return ResponseDTO.wrap(status, ex.getMessage());
        }
        errMsg.setLocation(exceptionLocation(ex));
        return ResponseDTO.wrap(status,errMsg);
    }

    /**
     * 异步任务异常拦截
     * @param ex
     * @return
     */
    @ExceptionHandler(AsyncBusinessException.class)
    public ResponseDTO<Object> handleAsyncException(AsyncBusinessException ex){
        HttpStatus status = HttpStatus.resolve(ex.getCode());
        ErrMsg errMsg = ex.getErrMsg();
        errMsg.setStatusCode(ex.getCode());
        // 如果无业务错误类定义，直接返回错误信息
        if (Validator.isEmpty(errMsg)){
            return ResponseDTO.wrap(status, ex.getMessage());
        }
        errMsg.setLocation(exceptionLocation(ex));
        return ResponseDTO.wrap(status,errMsg);
    }



    /**
     * 状态码 400 Bad Request 对应的异常
     * @param ex
     * @return
     */
    @ExceptionHandler({
            MissingServletRequestParameterException.class,
            IllegalArgumentException.class,
            TypeMismatchException.class,
            HttpMessageNotReadableException.class,
    })
    public ResponseDTO<Object> handleBadRequestException(Exception ex){
        ErrMsg errMsg = new ErrMsg();
        errMsg.setMessage(ex.getMessage());
        errMsg.setLocation(exceptionLocation(ex));
//        errMsg.setHref("http://www.baidu.com");
        if (Validator.isEmpty(errMsg.getStatusCode())){
            errMsg.setStatusCode(400);
        }
        return ResponseDTO.wrap(HttpStatus.BAD_REQUEST,errMsg);
    }
    // 参数校验错误单独处理
    @ExceptionHandler({
            MethodArgumentNotValidException.class
    })
    public ResponseDTO<Object> handleBadRequestException(MethodArgumentNotValidException ex){
        List<String> msgList = new ArrayList<>();
        for (FieldError fieldError : ex.getBindingResult().getFieldErrors()) {
            msgList.add(fieldError.getDefaultMessage());
        }
        ErrMsg errMsg = new ErrMsg();
        errMsg.setMessage(msgList.toString());
        errMsg.setLocation(exceptionLocation(ex));
        //errMsg.setHref("http://www.baidu.com");
        if (Validator.isEmpty(errMsg.getStatusCode())){
            errMsg.setStatusCode(400);
        }
        return ResponseDTO.wrap(HttpStatus.BAD_REQUEST,errMsg);
    }

    @ExceptionHandler({
            HttpClientErrorException.Unauthorized.class,
            AccessDeniedException.class
    })
    public ResponseDTO<Object> handleUnauthorizedException(Exception ex){
        ErrMsg errMsg = new ErrMsg();
        errMsg.setMessage(ex.getMessage());
        errMsg.setLocation(exceptionLocation(ex));
        //errMsg.setHref("http://www.baidu.com");
        if (Validator.isEmpty(errMsg.getStatusCode())){
            errMsg.setStatusCode(401);
        }
        return ResponseDTO.wrap(HttpStatus.UNAUTHORIZED,errMsg);
    }


    @ExceptionHandler({
            FileNotFoundException.class,
            NoHandlerFoundException.class
    })
    public ResponseDTO<Object> handleNotFoundException(Exception ex){
        ErrMsg errMsg = new ErrMsg();
        errMsg.setMessage(ex.getMessage());
        errMsg.setLocation(exceptionLocation(ex));
       // errMsg.setHref("http://www.baidu.com");
        if (Validator.isEmpty(errMsg.getStatusCode())){
            errMsg.setStatusCode(404);
        }
        return ResponseDTO.wrap(HttpStatus.NOT_FOUND,errMsg);
    }

    @ExceptionHandler({
            HttpRequestMethodNotSupportedException.class
    })
    public ResponseDTO<Object> handleMethodNotSupportException(Exception ex){
        ErrMsg errMsg = new ErrMsg();
        errMsg.setMessage(ex.getMessage());
        errMsg.setLocation(exceptionLocation(ex));
        //errMsg.setHref("http://www.baidu.com");
        if (Validator.isEmpty(errMsg.getStatusCode())){
            errMsg.setStatusCode(405);
        }
        return ResponseDTO.wrap(HttpStatus.METHOD_NOT_ALLOWED,errMsg);
    }

    @ExceptionHandler({
            HttpMediaTypeNotSupportedException.class
    })
    public ResponseDTO<Object> handleUnsupportedMediaTypeException(Exception ex){
        ErrMsg errMsg = new ErrMsg();
        errMsg.setMessage(ex.getMessage());
        errMsg.setLocation(exceptionLocation(ex));
        //errMsg.setHref("http://www.baidu.com");
        if (Validator.isEmpty(errMsg.getStatusCode())){
            errMsg.setStatusCode(415);
        }
        return ResponseDTO.wrap(HttpStatus.UNSUPPORTED_MEDIA_TYPE,errMsg);
    }

    @ExceptionHandler({
            NullPointerException.class,
            HttpMessageNotWritableException.class
    })
    public ResponseDTO<Object> handleRuntimeException(RuntimeException ex){
        if (ex instanceof NullPointerException){
            ErrMsg errMsg = new ErrMsg();
            errMsg.setMessage(ex.getClass().getName());
            errMsg.setLocation(exceptionLocation(ex));
            //errMsg.setHref("https://lucy.apisev.cn/#/err/");
            if (Validator.isEmpty(errMsg.getStatusCode())){
                errMsg.setStatusCode(500);
            }
            return ResponseDTO.wrap(HttpStatus.INTERNAL_SERVER_ERROR,errMsg);
        }
        return null;
    }
}
